//------------------------------------------------------------------------------
// <copyright file="AbstractDataAccess.cs" company="Damian Antonowicz">
//       copyright © 2010 Damian Antonowicz
// </copyright>
// <author>Damian Antonowicz</author>
// <email>poczta@damianantonowicz.pl</email>
//------------------------------------------------------------------------------

using System;
using System.IO;
using System.Text;
using System.Xml.Serialization;
using DAnton.Utilities.Extensions;
using Microsoft.Practices.EnterpriseLibrary.Caching;
using System.Diagnostics;
using System.Collections;
using System.Xml.Schema;
using System.Xml;
using System.Reflection;

namespace DotBeer.Data
{
    public abstract class AbstractDataAccess<T> where T : class
    {
        #region private members

        protected bool cacheData;

        #endregion

        #region constructors

        protected AbstractDataAccess()
        {
        }

        #endregion

        #region properties

        #endregion

        #region public methods

        public void Save(T obj)
        {
            if (obj == null)
                throw new ArgumentNullException("obj");

            try
            {
                this.BeforeSave(obj);
                string filePath = GetFilePath(obj);

                FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write);
                XmlSerializer xSer = new XmlSerializer(typeof(T));

                try
                {
                    xSer.Serialize(fs, obj);
                    this.AfterSave(obj);
                }
                catch
                {
                    throw;
                }
                finally
                {
                    fs.Close();
                }
            }
            catch
            {
                throw;
            }

            if (this.cacheData)
                this.CacheData(obj);
        }

        public T Load(params object[] list)
        {
            T obj = null;

            if (this.cacheData)
                obj = this.GetDataFromCache();

            if (obj == null)
            {
                string filePath = GetFilePath(list);

                if (File.Exists(filePath))
                {
                    FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
                    XmlSerializer xSer = new XmlSerializer(typeof(T));

                    XmlReaderSettings settings = new XmlReaderSettings();
                    settings.ValidationType = ValidationType.Schema;
                    settings.Schemas.Add(this.GetSchema());
                    settings.ValidationEventHandler += ((ob, args) =>
                    {
                        throw args.Exception;
                    });

                    XmlReader reader = XmlReader.Create(fs, settings);

                    try
                    {
                        this.BeforeLoad();
                        obj = (T)xSer.Deserialize(reader);
                        this.AfterLoad(obj);

                        if (this.cacheData)
                            this.CacheData(obj);
                    }
                    catch (InvalidOperationException ex)
                    {
                        if (ex.InnerException != null)
                            throw ex.InnerException;
                        else
                            throw;
                    }
                    catch
                    {
                        throw;
                    }
                    finally
                    {
                        fs.Close();
                    }
                }
                else
                {
                    throw new FileNotFoundException();
                }
            }

            return obj;
        }

        #endregion

        #region private methods

        protected abstract string GetFilePath(params object[] list);
        protected abstract void BeforeSave(T obj);
        protected virtual void AfterSave(T obj) { }
        protected virtual void BeforeLoad() { }
        protected virtual void AfterLoad(T obj) { }
        protected abstract string GetSchemaPath();

        private XmlSchema GetSchema()
        {
            Assembly assm = Assembly.Load("DotBeer.Data");
            Stream schemaStream = assm.GetManifestResourceStream(this.GetSchemaPath());
            XmlSchema schema = XmlSchema.Read(schemaStream, null);
            schemaStream.Close();

            return schema;
        }

        private void CacheData(T objectToCache)
        {
            ICacheManager cache = CacheFactory.GetCacheManager();
            cache.Add(typeof(T).Name, objectToCache, CacheItemPriority.NotRemovable, null, null);
        }

        private T GetDataFromCache()
        {
            ICacheManager cache = CacheFactory.GetCacheManager();
            return (T)cache.GetData(typeof(T).Name);
        }

        #endregion
    }
}
